import 'dart:async';
// backcompatibility for Flutter < 3.3
// ignore: unnecessary_import
import 'dart:typed_data';

import 'package:flutter/services.dart';
import 'package:meta/meta.dart';

import '../../sentry_flutter.dart';
import '../replay/replay_config.dart';
import 'native_app_start.dart';
import 'sentry_native_binding.dart';
import 'sentry_native_invoker.dart';
import 'sentry_safe_method_channel.dart';

/// Provide typed methods to access native layer via MethodChannel.
@internal
class SentryNativeChannel
    with SentryNativeSafeInvoker
    implements SentryNativeBinding {
  @override
  final SentryFlutterOptions options;

  @protected
  final SentrySafeMethodChannel channel;

  SentryNativeChannel(this.options)
      : channel = SentrySafeMethodChannel(options);

  void _logNotSupported(String operation) => options.log(
      SentryLevel.debug, 'SentryNativeChannel: $operation is not supported');

  @override
  Future<void> init(Hub hub) async {
    assert(false, 'init should not be used through method channels.');
  }

  @override
  Future<void> close() async => channel.invokeMethod('closeNativeSdk');

  @override
  FutureOr<NativeAppStart?> fetchNativeAppStart() async {
    assert(false,
        'fetchNativeAppStart should not be used through method channels.');
    return null;
  }

  @override
  bool get supportsCaptureEnvelope => true;

  @override
  FutureOr<void> captureEnvelope(
      Uint8List envelopeData, bool containsUnhandledException) {
    assert(
        false, 'captureEnvelope should not be used through method channels.');
  }

  @override
  FutureOr<void> captureStructuredEnvelope(SentryEnvelope envelope) {
    throw UnsupportedError('Not supported on this platform');
  }

  @override
  bool get supportsLoadContexts => true;

  @override
  FutureOr<Map<String, dynamic>?> loadContexts() {
    assert(false, 'loadContexts should not be used through method channels.');
    return null;
  }

  @override
  FutureOr<void> setUser(SentryUser? user) async {
    assert(false, 'setUser should not be used through method channels.');
  }

  @override
  FutureOr<void> addBreadcrumb(Breadcrumb breadcrumb) async {
    assert(false, 'addBreadcrumb should not be used through method channels.');
  }

  @override
  FutureOr<void> clearBreadcrumbs() async {
    assert(
        false, 'clearBreadcrumbs should not be used through method channels.');
  }

  @override
  FutureOr<void> setContexts(String key, dynamic value) {
    assert(false, 'setContexts should not be used through method channels.');
  }

  @override
  FutureOr<void> removeContexts(String key) {
    assert(false, 'removeContexts should not be used through method channels.');
  }

  @override
  FutureOr<void> setExtra(String key, dynamic value) {
    assert(false, 'setExtra should not be used through method channels.');
  }

  @override
  FutureOr<void> removeExtra(String key) {
    assert(false, 'removeExtra should not be used through method channels.');
  }

  @override
  FutureOr<void> setTag(String key, String value) {
    assert(false, 'setTag should not be used through method channels.');
  }

  @override
  FutureOr<void> removeTag(String key) {
    assert(false, 'removeTag should not be used through method channels.');
  }

  @override
  int? startProfiler(SentryId traceId) =>
      throw UnsupportedError('Not supported on this platform');

  @override
  Future<void> discardProfiler(SentryId traceId) =>
      channel.invokeMethod('discardProfiler', traceId.toString());

  @override
  Future<Map<String, dynamic>?> collectProfile(
          SentryId traceId, int startTimeNs, int endTimeNs) =>
      channel.invokeMapMethod<String, dynamic>('collectProfile', {
        'traceId': traceId.toString(),
        'startTime': startTimeNs,
        'endTime': endTimeNs,
      });

  @override
  FutureOr<List<DebugImage>?> loadDebugImages(SentryStackTrace stackTrace) {
    assert(
        false, 'loadDebugImages should not be used through method channels.');
    return null;
  }

  @override
  FutureOr<int?> displayRefreshRate() {
    assert(false,
        'displayRefreshRate should not be used through method channels.');
    return null;
  }

  @override
  FutureOr<void> pauseAppHangTracking() {
    assert(false,
        'pauseAppHangTracking should not be used through method channels.');
  }

  @override
  FutureOr<void> resumeAppHangTracking() {
    assert(false,
        'resumeAppHangTracking should not be used through method channels.');
  }

  @override
  FutureOr<void> nativeCrash() {
    assert(false, 'nativeCrash should not be used through method channels.');
  }

  @override
  bool get supportsReplay => false;

  @override
  SentryId? get replayId => null;

  @override
  FutureOr<void> setReplayConfig(ReplayConfig config) {
    assert(
        false, 'setReplayConfig should not be used through method channels.');
  }

  @override
  FutureOr<SentryId> captureReplay() {
    assert(false, 'captureReplay should not be used through method channels.');
    return SentryId.empty();
  }

  @override
  FutureOr<void> captureSession() {
    _logNotSupported('capturing session');
  }

  @override
  FutureOr<void> startSession({bool ignoreDuration = false}) {
    _logNotSupported('starting session');
  }

  @override
  FutureOr<Map<dynamic, dynamic>?> getSession() {
    _logNotSupported('getting session');
    return null;
  }

  @override
  FutureOr<void> updateSession({int? errors, String? status}) {
    _logNotSupported('updating session');
  }
}
